package cn.uncode.dal.criteria;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import cn.uncode.dal.annotation.Table;
import cn.uncode.dal.criteria.Criterion.Condition;

/**
 * 查询条件封装类
 * @author Juny
 *
 */
public class QueryCriteria {
	
	private String database;
    
    private String table;
    
    private String orderByClause;

    private String groupBy;
    
    private String having;
    
    private boolean distinct;

    private List<Criteria> oredCriteria;

    private boolean selectOne;
    
    private int pageIndex = 1;
    
    private int pageSize = 15;
    
    private int recordIndex = 0;
    
    private int limit;
    
    private Object version;

    private boolean shardTransaction;
    
    public QueryCriteria() {
        oredCriteria = new ArrayList<Criteria>();
    }
    
    /**
     * 深度复制新的查询对象
     */
    @Override
    public QueryCriteria clone(){
    	QueryCriteria clone = new QueryCriteria();
    	clone.setDatabase(database);
    	clone.setTable(table);
    	clone.setOrderByClause(orderByClause);
    	clone.setGroupBy(groupBy);
    	clone.setDistinct(distinct);
    	clone.setOrderByClause(orderByClause);
    	clone.setSelectOne(selectOne);
    	clone.setPageIndex(pageIndex);
    	clone.setPageSize(pageSize);
    	clone.setRecordIndex(recordIndex);
    	clone.setLimit(limit);
    	clone.setVersion(version);
    	clone.setHaving(having);
        clone.setShardTransaction(shardTransaction);
        if(this.oredCriteria != null && this.oredCriteria.size()>0){
            for (Criteria criteria : this.oredCriteria) {
                clone.oredCriteria.add(criteria.clone());
            }
        }
    	return clone;
    }

    /**
     * 设置排序字段
     * @param orderByClause 如：age DESC
     */
    @Deprecated
    public void setOrderByClause(String orderByClause) {
        this.orderByClause = orderByClause;
    }
    
    /**
     * 设置排序字段
     * @param orderClumn 如：age
     */
    public void setDescOrderByClause(String orderClumn) {
        this.orderByClause = orderClumn + " DESC";
    }
    
    /**
     * 设置排序字段
     * @param orderClumn 如：age
     */
    public void setAscOrderByClause(String orderClumn) {
        this.orderByClause = orderClumn + " ASC";
    }

    /**
     * 查询排序字符串
     * @return 排序字符串
     */
    public String getOrderByClause() {
        return orderByClause;
    }
    
    /**
     * 查询分组字段
     * @return 分组字段名
     */
    public String getGroupBy() {
		return groupBy;
	}

    /**
     * 设置分组字段
     * @param groupBy 如：age
     */
	public void setGroupBy(String groupBy) {
		this.groupBy = groupBy;
	}

    /**
     * 查询having条件
     * @return having条件字符串
     */
	public String getHaving() {
		return having;
	}

    /**
     * 设置having条件
     * @param having 如：age>10
     */
	public void setHaving(String having) {
		this.having = having;
	}

    /**
     * 设置是否去重
     * @param distinct 默认为false
     */
	public void setDistinct(boolean distinct) {
        this.distinct = distinct;
    }

    /**
     * 查询是否去重
     * @return true或false
     */
    public boolean getDistinct() {
        return distinct;
    }
    
    /**
     * 查询分页的当前页码
     * @return 当前页码，最小值为1 
     */
    public int getPageIndex() {
        return pageIndex;
    }

    /**
     * 设置查询的当前页码
     * @param pageIndex 最小值1，高于最大页数查询时会自动计算为最大页码值，查询结果为尾页值。分页必填。
     */
    public void setPageIndex(int pageIndex) {
    	if(pageIndex > 0) {
    		this.pageIndex = pageIndex;
    	}
    }

    /**
     * 查询每页的记录数
     * @return 每页的记录数
     */
    public int getPageSize() {
        return pageSize;
    }

    /**
     * 设置每页的记录数
     * @param pageSize 每页的记录数，分页必填。
     */
    public void setPageSize(int pageSize) {
    	if(pageSize > 0) {
    		this.pageSize = pageSize;
    	}
    }

    /**
     * 查询当前sql表名
     * @return sql表名
     */
    public String getTable() {
        return table;
    }

    /**
     * 设置查询的表名
     * @param table 表名
     */
    public void setTable(String table) {
        this.table = table.toLowerCase().trim();
    }
    
    /**
     * 查询当前数据库名称
     * @return 数据库名称
     */
    public String getDatabase() {
		return database;
	}

    /**
     * 设置查询的数据库名称
     * @param database 数据库名称
     */
	public void setDatabase(String database) {
		this.database = database;
	}

    /**
     * 查询有版本控制记录的当前版本，一般用于update操作
     * @return 当前版本值
     */
	public Object getVersion() {
		return version;
	}

    /**
     * 设置有版本控制记录的当前版本
     * @param version 当前版本
     */
	public void setVersion(Object version) {
		this.version = version;
	}
	
    /**
     * 查询limit的起始值
     * @return limit的起始值
     */
	public int getRecordIndex() {
		return recordIndex;
	}

    /**
     * 设置limit的起始值，必须与setLimit方法一起使用，不要同setPage***相关方法一起使用
     * @param recordIndex limit的起始值，该值为10时，结果为：limit 10,20
     */
	public void setRecordIndex(int recordIndex) {
		this.recordIndex = recordIndex;
	}

    /**
     * 查询limit的值
     * @return limit的值
     */
	public int getLimit() {
		return limit;
	}

    /**
     * 设置limit值，必须与setRecordIndex方法一起使用，不要同setPage***相关方法一起使用
     * @param limit limit值，该值为20时，结果为：limit 10,20
     */
	public void setLimit(int limit) {
		this.limit = limit;
	}

	/**
	 * 获取是否是分表且带事务
     * @return 是否是分表且带事务,true:有
	 */
    public boolean getShardTransaction() {
        return shardTransaction;
    }
    /**
     * 设置是否是分表且带事务
     * @param shardTransaction true:有
     */
    public void setShardTransaction(boolean shardTransaction) {
        this.shardTransaction = shardTransaction;
    }

    /**
     * 设置查询的表名
     * @param clazz DTO类对象，默认类名与表名必须一致，不一致时加@Table注解
     */
	public void setTable(Class<?> clazz) {
		Table table = clazz.getAnnotation(Table.class);
        String name = "";
        if(table != null){
        	name = table.name();
        }else{
        	name = clazz.getName();
        	name = name.substring(name.lastIndexOf(".") + 1);
        }
        this.table = name.toLowerCase().trim();
    }
    
	/**
	 * 查询所有查询条件集合
	 * @return 查询条件集合
	 */
    public List<Criteria> getOredCriteria() {
        return oredCriteria;
    }

    /**
     * 添加一个OR的条件到现有查询条件对象中
     * 最终结果为：(...) OR (...)
     * @param criteria 查询条件
     */
    public void or(Criteria criteria) {
        oredCriteria.add(criteria);
    }

    /**
     * 现有查询条件对象中生成一个OR的条件
     * 最终结果为：(...) OR (...)
     * @return 查询条件
     */
    public Criteria or() {
        Criteria criteria = createCriteriaInternal();
        oredCriteria.add(criteria);
        return criteria;
    }

    /**
     * 创建查询条件，如果已经存在，就生成一个新的并添加到列表中
     * @return 查询条件
     */
    public Criteria createCriteria() {
        Criteria criteria = createCriteriaInternal();
        if (oredCriteria.size() == 0) {
            oredCriteria.add(criteria);
        }
        return criteria;
    }

    protected Criteria createCriteriaInternal() {
        Criteria criteria = new Criteria();
        return criteria;
    }

    /**
     * 清除所有查询条件
     */
    public void clear() {
        oredCriteria.clear();
        orderByClause = null;
        distinct = false;
    }

    /**
     * 设置是否只查一条数据，性能优化
     * @param selectOne 为true时，会添加limit 0,1到查询语句尾部
     */
    public void setSelectOne(boolean selectOne) {
        this.selectOne = selectOne;
    }

	/**
	 * 查询是否只查一条数据
	 * @return true或false
	 */
    public boolean getSelectOne() {
        return this.selectOne;
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((orderByClause == null) ? 0 : orderByClause.hashCode());
        result = prime * result + ((database == null) ? 0 : database.hashCode());
        result = prime * result + ((table == null) ? 0 : table.hashCode());
        result = prime * result + ((version == null) ? 0 : version.hashCode());
        result = prime * result + ((groupBy == null) ? 0 : groupBy.hashCode());
        result = prime * result + (distinct ? 1231 : 1237);
        result = prime * result + (selectOne ? 1231 : 1237);
        result = prime * result + (shardTransaction ? 1231 : 1237);
        result = prime * result + pageIndex + pageSize;
        result = prime * result + recordIndex + limit;
        if (oredCriteria != null) {
            for (Criteria criteria : oredCriteria) {
                for (Criterion cter : criteria.getAllCriteria()) {
                	result = prime * result + ((cter== null)?0:cter.hashCode());
                }
            }
        }
        return result;
    }
    
    @Override
    public String toString(){
    	StringBuffer sb = new StringBuffer();
    	if(StringUtils.isNotEmpty(database)){
    		sb.append("database:").append(database).append(",");
    	}
    	if(StringUtils.isNotEmpty(table)){
    		sb.append("table:").append(table).append(",");
    	}
    	if(StringUtils.isNotEmpty(orderByClause)){
    		sb.append("orderByClause:").append(orderByClause).append(",");
    	}
    	if(StringUtils.isNotEmpty(groupBy)){
    		sb.append("groupBy:").append(groupBy).append(",");
    	}
    	sb.append("distinct:").append(distinct).append(",");
    	if(null != oredCriteria){
    		sb.append("oredCriteria:").append(oredCriteria).append(",");
    	}
    	sb.append("selectOne:").append(selectOne).append(",");
    	sb.append("pageIndex:").append(pageIndex).append(",");
    	sb.append("pageSize:").append(pageSize).append(",");
    	sb.append("recordIndex:").append(recordIndex).append(",");
    	sb.append("shardTransaction:").append(shardTransaction).append(",");
    	if(null != version){
    		sb.append("version:").append(version).append(",");
    	}
    	sb.deleteCharAt(sb.lastIndexOf(","));
        
    	return sb.toString();
    }

    protected abstract static class GeneratedCriteria {
        protected List<Criterion> criteria;

        protected GeneratedCriteria() {
            super();
            criteria = new ArrayList<Criterion>();
        }

        public boolean isValid() {
            return criteria.size() > 0;
        }
        
        /**
         * 查询所有条件列表
         * @return 条件列表
         */
        public List<Criterion> getAllCriteria() {
            return criteria;
        }

        /**
         * 查询所有条件列表
         * @return 条件列表
         */
        public List<Criterion> getCriteria() {
            return criteria;
        }
        
        protected void addCriterion(String sql) {
            if (StringUtils.isEmpty(sql)) {
                throw new RuntimeException("Sql cannot be null");
            }
            criteria.add(new Criterion(sql));
        }
        
        protected void addCriterion(Condition condition, String column) {
            if (condition == null || StringUtils.isEmpty(column)) {
                throw new RuntimeException("Column for condition cannot be null");
            }
            criteria.add(new Criterion(condition, column));
        }
        
        protected void addCriterion(Condition condition, Object value, String typeHandler, String column) {
            if (value == null || condition == null || StringUtils.isEmpty(column)) {
                throw new RuntimeException("Value for condition cannot be null");
            }
            criteria.add(new Criterion(condition, value, typeHandler, column));
        }

        protected void addCriterion(Condition condition, Object value1, Object value2, String typeHandler, String column) {
            if (value1 == null || value2 == null) {
                throw new RuntimeException("Between values for " + column + " cannot be null");
            }
            criteria.add(new Criterion(condition, value1, value2, typeHandler, column));
        }
        
        /**
         * 直接拼接字段相关的sql语句
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.SQL, sql);
         * 	criteria.append(sql);
         * </pre>
         * @param sql sql字符串
         * @return 条件对象
         */
        public Criteria andColumnSql(String sql) {
            addCriterion(sql);
            return (Criteria) this;
        }

        /**
         * 设置字段为NULL的查询条件[is null]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.IS_NULL, column);
         * 	criteria.append(Condition.IS_NULL, column, null);
         * </pre>
         * @param column 字段名称
         * @return 条件对象
         */
        public Criteria andColumnIsNull(String column) {
            addCriterion(Condition.IS_NULL, column);
            return (Criteria) this;
        }

        /**
         * 设置字段不为NULL的查询条件[is not null]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.IS_NOT_NULL, column);
         * 	criteria.append(Condition.IS_NOT_NULL, column, null);
         * </pre>
         * @param column 字段名称
         * @return 条件对象
         */
        public Criteria andColumnIsNotNull(String column) {
            addCriterion(Condition.IS_NOT_NULL, column);
            return (Criteria) this;
        }

        /**
         * 设置字段等于的查询条件[==]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.EQUAL, column, value);
         * 	criteria.append(column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnEqualTo(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.EQUAL, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }

        /**
         * 设置字段不等于的查询条件[<>]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.NOT_EQUAL, column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnNotEqualTo(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.NOT_EQUAL, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }

        /**
         * 设置字段大于的查询条件[>]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.GREATER_THAN, column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnGreaterThan(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.GREATER_THAN, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }

        /**
         * 设置字段大于等于的查询条件[>=]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.GREATER_THAN_OR_EQUAL, column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnGreaterThanOrEqualTo(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.GREATER_THAN_OR_EQUAL, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }

        /**
         * 设置字段小于的查询条件[<]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.LESS_THAN, column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnLessThan(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.LESS_THAN, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }

        /**
         * 设置字段小于等于的查询条件[<=]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.LESS_THAN_OR_EQUAL, column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnLessThanOrEqualTo(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.LESS_THAN_OR_EQUAL, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }
        
        /**
         * 设置字段in的查询条件[in(...)]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.IN, column, values);
         * </pre>
         * @param column 字段名称
         * @param values 值的集合
         * @return 条件对象
         */
        public <T extends Object> Criteria andColumnIn(String column, Collection<T> values) {
        	if(null != values && values.size() > 0){
        		addCriterion(Condition.IN, values, values.getClass().getName(), column);
        	}
            return (Criteria) this;
        }

        /**
         * 设置字段not in的查询条件[not in(...)]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.NOT_IN, column, values);
         * </pre>
         * @param column 字段名称
         * @param values 值的集合
         * @return 条件对象
         */
        public <T extends Object> Criteria andColumnNotIn(String column, Collection<T> values) {
        	if(null != values && values.size() > 0){
        		addCriterion(Condition.NOT_IN, values, values.getClass().getName(), column);
        	}
            return (Criteria) this;
        }
        
        /**
         * 设置字段like的查询条件[like %**%]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.LIKE, column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnLike(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.LIKE, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }
        
        /**
         * 设置字段not like的查询条件[not like %**%]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.NOT_LIKE, column, value);
         * </pre>
         * @param column 字段名称
         * @param value 值
         * @return 条件对象
         */
        public Criteria andColumnNotLike(String column, Object value) {
        	if(null != value){
        		addCriterion(Condition.NOT_LIKE, value, value.getClass().getName(), column);
        	}
            return (Criteria) this;
        }

        /**
         * 设置字段between的查询条件[between]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.BETWEEN, column, value1, value2);
         * </pre>
         * @param column 字段名称
         * @param value1 起始值
         * @param value2 终值
         * @return 条件对象
         */
        public Criteria andColumnBetween(String column, Object value1, Object value2) {
            addCriterion(Condition.BETWEEN, value1, value2, value1.getClass().getName(), column);
            return (Criteria) this;
        }

        /**
         * 设置字段not between的查询条件[not between]
         * <pre>
         * 类同于：
         * 	criteria.append(Condition.NOT_BETWEEN, column, value1, value2);
         * </pre>
         * @param column 字段名称
         * @param value1 起始值
         * @param value2 终值
         * @return 条件对象
         */
        public Criteria andColumnNotBetween(String column, Object value1, Object value2) {
            addCriterion(Condition.NOT_BETWEEN, value1, value2, value1.getClass().getName(), column);
            return (Criteria) this;
        }

    }

    public class Criteria extends GeneratedCriteria {

        protected Criteria() {
            super();
        }
        /**
         * 快速封装查询条件
         * @param condition Condition条件枚举
         * @param filed 字段
         * @param value 值
         * @return 条件对象
         */
        public Criteria append(Condition condition, String filed, Object value){
        	if(condition == Condition.EQUAL){
        		andColumnEqualTo(filed, value);
    		}else if(condition == Condition.NOT_EQUAL){
    			andColumnNotEqualTo(filed, value);
    		}else if(condition == Condition.GREATER_THAN){
    			andColumnGreaterThan(filed, value);
    		}else if(condition == Condition.GREATER_THAN_OR_EQUAL){
    			andColumnGreaterThanOrEqualTo(filed, value);
    		}else if(condition == Condition.LESS_THAN){
    			andColumnLessThan(filed, value);
    		}else if(condition == Condition.LESS_THAN_OR_EQUAL){
    			andColumnLessThanOrEqualTo(filed, value);
    		}else if(condition == Condition.LIKE){
    			andColumnLike(filed, value);
    		}else if(condition == Condition.IS_NULL){
    			andColumnIsNull(filed);
    		}else if(condition == Condition.IS_NOT_NULL){
    			andColumnIsNotNull(filed);
    		}else if(condition == Condition.SQL){
    			andColumnSql(value.toString());
    		}else{
    			andColumnEqualTo(filed, value);
    		}
        	return this;
        }
        
        /**
         * 快速封装查询条件[between或not between]
         * @param condition Condition条件枚举
         * @param filed 字段
         * @param value1 起始值
         * @param value2 终值
         * @return 条件对象
         */
        public Criteria append(Condition condition, String filed, Object value1, Object value2){
        	if(condition == Condition.BETWEEN){
        		andColumnBetween(filed, value1, value2);
    		}else if(condition == Condition.NOT_BETWEEN){
    			andColumnNotBetween(filed, value1, value2);
    		}
        	return this;
        }
        
        /**
         * 快速封装查询条件[in或not in]
         * @param condition Condition条件枚举
         * @param filed 字段
         * @param values 值的集合
         * @return 条件对象
         */
        public <T extends Object> Criteria append(Condition condition, String filed, Collection<T> values){
        	if(condition == Condition.IN){
        		andColumnIn(filed, values);
    		}else if(condition == Condition.NOT_IN){
    			andColumnNotIn(filed, values);
    		}
        	return this;
        }
        
        /**
         * 快速封装无值的查询条件[is null或is not null]
         * @param condition Condition条件枚举
         * @param filed 字段
         * @return 条件对象
         */
        public Criteria append(Condition condition, String filed){
        	return append(condition, filed, null);
        }
        
        /**
         * 快速封装等于的查询条件[==]
         * @param filed 字段
         * @param value 值
         * @return 条件对象
         */
        public Criteria append(String filed, Object value){
        	return append(Condition.EQUAL, filed, value);
        }
        
        /**
         * 快速封装sql查询条件
         * @param sql sql片段
         * @return 条件对象
         */
        public Criteria append(String sql){
        	return append(Condition.SQL, null, sql);
        }
        
        @Override
        public String toString(){
        	if(null != criteria){
        		return criteria.toString();
        	}
        	return null;
        }

        @Override
        protected Criteria clone() {
            Criteria criteria = new Criteria();
            if(this.criteria != null && this.criteria.size()>0){
                for (Criterion criterion : this.criteria) {
                    criteria.criteria.add(criterion.clone());
                }
            }
            return criteria;
        }
    }
    
    
    

}